package za.org.jcicada.nodeproxy.impl;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import za.org.jcicada.genss.sensor.impl.InvalidSensorValue;
import za.org.jcicada.genss.sensor.impl.Sensor;
import za.org.jcicada.genss.sensor.impl.StatusStrategyFactory;
import za.org.jcicada.genss.sensor.impl.TypeFloat;
import za.org.jcicada.genss.sensor.impl.TypeInt;

/**
 * Class used to measure node resource usage
 * @author rcrida
 */
public class ResourceUsage {

    /** Logger object used for logging messages. */
    private static final Log logger = LogFactory.getLog(ResourceUsage.class);
    
    private TimerTask task;
    
    private OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();

    /** Sensor representing the number of available cpus. */
    private Sensor cpuCountSensor = new Sensor(TypeInt.getInstance(0, Integer.MAX_VALUE), "cpuCount", "Number of cpus", "");

    /** Sensor representing the load average per cpu of the system. */
    private Sensor loadSensor = new Sensor(TypeFloat.getInstance(0, 100), "load", "Node load average per cpu sensor", "");
    
    private Map<File, Sensor> partitionSensors = new HashMap<File, Sensor>();
    
    public ResourceUsage() {
    	for (File file : File.listRoots()) {
    		Sensor fileSensor = new Sensor(TypeFloat.getInstance(0, 100), "partition" + file.getPath().replace('\\', '/'), "Partition percentage usage for " + file.getPath(), "%");
    		partitionSensors.put(file, fileSensor);
    	}
    }
    
	public Sensor[] getSensors() {
		Sensor[] sensors = new Sensor[2 + partitionSensors.size()];
		sensors[0] = cpuCountSensor;
		sensors[1] = loadSensor;
		System.arraycopy(partitionSensors.values().toArray(), 0, sensors, 2, partitionSensors.size());
		return sensors;
	}
	
    public void initialize(Timer timer, long period_ms) {
    	task = new TimerTask() {

			@Override
			public void run() {
				try {
					cpuCountSensor.setValue(new Integer(mxBean.getAvailableProcessors()));
				} catch (InvalidSensorValue ex) {
					logger.error(ex);
				}
				try {
					loadSensor.setValue(new Float(mxBean.getSystemLoadAverage() / mxBean.getAvailableProcessors()));
				} catch (InvalidSensorValue ex) {
					logger.error(ex);
				}
				for (Map.Entry<File, Sensor> entry : partitionSensors.entrySet()) {
					try {
						long totalSpace = Math.max(1, entry.getKey().getTotalSpace());
						long freeSpace = entry.getKey().getFreeSpace();
						float percent = (totalSpace - freeSpace) * 100.0f / totalSpace;
						entry.getValue().setValue(percent);
					} catch(InvalidSensorValue ex) {
						logger.error(ex);
					}
				}
			}
			
		};
		timer.scheduleAtFixedRate(task, 0L, period_ms);
    }
    
    public void configure(NodeProxyDynamicConfiguration config) {
    	cpuCountSensor.setStatusStrategy(StatusStrategyFactory.createStatusStrategy(cpuCountSensor, config.cpuCountStatusStrategy));
    	loadSensor.setStatusStrategy(StatusStrategyFactory.createStatusStrategy(loadSensor, config.loadStatusStrategy));
    	for (Sensor partitionSensor : partitionSensors.values()) {
    		partitionSensor.setStatusStrategy(StatusStrategyFactory.createStatusStrategy(partitionSensor, config.partitionStatusStrategy));
    	}
    }
    
    public void shutdown() {
    	if (task != null)
    		task.cancel();
    }

}
